package org.jackysoft.data;

import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.jackysoft.annotations.OptionalText;
import org.jackysoft.annotations.OptionalValue;
import org.jackysoft.config.CommonConfiguration;
import org.jackysoft.entity.BaseOption;
import org.jackysoft.entity.Identifiable;
import org.jackysoft.google.collection.ZTreeNode;
import org.jackysoft.query.Query;
import org.jackysoft.util.Page;
import org.jackysoft.util.StringUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import com.google.common.collect.Lists;

public abstract class AbstractDataProvider<ID extends Serializable,B extends Identifiable> implements IDataProvider<ID,B> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6829272880152190727L;
    protected static final Log log = LogFactory.getLog(AbstractDataProvider.class);
	protected SessionFactory sessionFactory;
	private CommonConfiguration config;
	protected Class type = null;
	protected String luceneExpress;
	protected Analyzer chineseAnalyzer;	
	protected String optionContruct;
	protected Query query;
	protected SqlSessionTemplate sqlSession;
	
	@Override
	public void detach() {
	
	}
	

	public AbstractDataProvider() {}
	
	public AbstractDataProvider(Class type) {
		this.type = type;
		parseOptHql(type);
	}


	protected void parseOptHql(Class tpt){
		String label = null,value = null;
		Method[] mds = tpt.getMethods();
	    for(Method m:mds){
	    	if(m.isAnnotationPresent(OptionalText.class)){
	            label = StringUtils.lowerFirstChar(m.getName().substring(3));
	    	    continue;
	    	}
	    	if(m.isAnnotationPresent(OptionalValue.class)){
	    		value = StringUtils.lowerFirstChar(m.getName().substring(3));
	    		continue;
	    	}
	    	if(label!=null && value!=null)break;
	    }
	    optionContruct = String.format(" new org.jackysoft.entity.BaseOption(t.%s,t.%s) ", label,value);
	
	}

	@Override
	public Iterable<B> getData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		if (smap.containsKey("_search")) {
			log.info("搜索数据...");
			return searchData(start, size, listAll, sortInfo, smap);
		}
		return listData(start, size, listAll, sortInfo, smap);
	}

	@Override
	public int getSize(SortInfo sortInfo, Map<String, Object> smap) {
		if (smap.containsKey("_search")) {
			return searchDataSize(sortInfo, smap);
		}
		return listDataSize(sortInfo, smap);
	}

	protected int listDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		Session session = getSession();
		Criteria crt = session.createCriteria(this.type);
		crt.setProjection(Projections.rowCount());

		Object o = crt.uniqueResult();

		return o == null ? 0 : Integer.parseInt(o.toString());
	}

	/**
	 * 数据列表,重载以符合自己的要求
	 * */
	protected List<B> listData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		Session session = getSession();
		Criteria crt = session.createCriteria(this.type);
		if (sortInfo != null) {
			if (sortInfo.getSortOrder().equals(SortOrder.asc)) {
				crt.addOrder(Property.forName(sortInfo.getProperty()).asc());
			}else{
				crt.addOrder(Property.forName(sortInfo.getProperty()).desc());
			}
		}
		if (!listAll) {
			crt.setFirstResult(start);
			crt.setMaxResults(size);
		}
		return crt.list();
	}

	/**
	 * 搜索数据,重载以符合搜索要求
	 * */
	protected List<B> searchData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		return null;
	}

	protected int searchDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		
		return 0;
	}

	
	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void sorts(Collection<B> beans) {
		if (beans == null || beans.isEmpty())
			return;
		Object ec = beans.iterator().next();
		if (ec instanceof Comparable) {
			List clist = Lists.newArrayList();
			clist.addAll(beans);
			Collections.sort(clist);			
		}

	}

	@Override
	public void sorted(Collection<B> beans) throws Exception {
		if (beans == null || beans.isEmpty())
			return;
		Object to = beans.iterator().next();
		Method[] fs = to.getClass().getMethods();
		String idpro = "id";
		for (Method f : fs) {
			if (f.isAnnotationPresent(javax.persistence.Id.class)) {
				idpro = StringUtils.lowerFirstChar(f.getName().substring(3));
				break;
			}
		}
		String hql = "update " + entitytable()
				+ " set sort=:sort where " + idpro + "=:id";
		Session sess = getSession();
		int count = 0;

		for (Object o : beans) {
			Object s = PropertyUtils.getProperty(o, "sort");
			Object id = PropertyUtils.getProperty(o, idpro);
			sess.createQuery(hql)
					.setInteger("sort", Integer.parseInt(s.toString()))
					.setString("id", id.toString()).executeUpdate();
			if (++count % 10 == 0) {
				sess.flush();
			}
		}

	}
	
	
	
	@Override
	public int count() throws DataAccessException {
		Object obj = getSession().createQuery("select count(*) as countNum from "+entitytable()).iterate().next();
		return Integer.parseInt(obj.toString());
	}


	/**
	 * 重载以构造lucene表达式....
	 * */
	public String getLuceneExpress(Map<String, Object> smap) {
		return luceneExpress;
	}
	
	@Resource(name="sessionFactory")
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	@Override
	public Page getsPage(Integer pindex, Integer rows) throws DataAccessException {
		return forPage(pindex,rows,getQuery());
	}
	
	
	@Override
	public Page query(Query qc, Integer pindex, Integer rows)
			throws DataAccessException {
		
		return forPage(pindex,rows,qc);
	}
	
	@Override
	public Page  details(Query qc, Integer pindex, Integer rows){
		return forPage(pindex,rows,qc);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	Collection<B> gets(int start,int rows,Query q){
		q.setPrefix("a");
		String hql = String.format("from %s a where %s", entitytable(),q.queryString());
		List list = getSession().createQuery(hql).setFirstResult(start).setMaxResults(rows).list();
	    return list;
	}
	
	
	Page forPage(Integer pindex,Integer rows,Query q){
		Page page = new Page();
		if(rows==0)rows = 10;	
		double count = q==null?count():count(q);
		page.setPage(pindex);
	    page.setRecords(new Double(count).intValue());
	    int start = pindex*rows;
	    if(start<0) start=0;
	    if(q!=null){	       
	    	page.setRows(gets(start, rows,q));	    
	    }else{
	    	page.setRows(gets(start, rows));
	    }	  
	    page.setOffset(rows);
		return page;
	}

	@Override
	public int count(Query qc) throws DataAccessException {
		String sql = String.format("select count(*) from %s where %s", entitytable(),qc.queryString());
		Object obj = getSession().createQuery(sql).iterate().next(); 
		return obj==null?0:Integer.parseInt(obj.toString());
	}

	
	@Override
	public Collection<ZTreeNode> ztree(B bean) throws DataAccessException {
	    return null;
	}


	@Override
	public void create(File[] files, String[] names)
			throws Exception {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void create(File file, String name) throws Exception {
		this.create(file, name, null);
		
	}

	
	@Override
	public void create(File file, String name, B bean) throws Exception {
		// TODO Auto-generated method stub
		
	}


	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Collection<BaseOption> getOptions(Query query) {
		String cond = query==null?"":"where "+query.queryString();
		if(optionContruct==null)parseOptHql(getType());
		String hql = HQL("select %s from %s t %s ",optionContruct,entitytable(),cond);
	    List list = getSession().createQuery(hql).list();
		return list;
	}


	protected Session getSession() {
		Session session = SessionFactoryUtils.getSession(sessionFactory, true);
		return session;
	}
	public Class getType() {
		return type;
	}

	public void setType(Class type) {
		this.type = type;

	}
	
	public String HQL(String hql,Object... args){
		return String.format(hql, args);	
	}
	
	public String qualifiedentitytable(){
		AbstractEntityPersister classMetadata = (AbstractEntityPersister)sessionFactory.getClassMetadata(getType());  
	    return classMetadata.getTableName();	
	}
	
	public String entitytable(){
	   
		
	    return getType().getName();	
	
	}
	
	public String table(Class<?> cls){
		return cls.getName();
	}
	
	public Analyzer getChineseAnalyzer() {
		return chineseAnalyzer;
	}

	@Resource
	public void setChineseAnalyzer(Analyzer chineseAnalyzer) {
		this.chineseAnalyzer = chineseAnalyzer;
	}

	@Resource(name = "commonConfiguration")
	public void setConfig(CommonConfiguration config) {
		this.config = config;
	
	}
	
	protected CommonConfiguration getConfig(){return this.config;}


	public Query getQuery() {
		return query;
	}


	public void setQuery(Query query) {
		this.query = query;
	}

	@Resource
	public void setSqlSession(SqlSessionTemplate sqlSession) {
		this.sqlSession = sqlSession;
	}
	
	

}
